package org.opens.lambda.collector;

import org.junit.Test;
import org.opens.apache.http.HttpUtil;
import org.opens.lambda.UserEntity;

import java.util.*;
import java.util.stream.Collectors;

public class CollectorExample {

    /**
     * 说明:
     *      1. Collectors的作用之一就是聚合, 将stream中的值按照一定的规则聚合成不同的结果.
     *      2. 将结果封装到Optional中, 使用orElse设置默认值, 省略判断null.
     */
    @Test
    public void test1() {
        List<UserEntity> list = UserEntity.getList(10);
        List<UserEntity> collect = list.stream().filter(x -> x.getAge() >= 18).collect(Collectors.toList());
        System.out.println(Optional.ofNullable(collect).orElse(new ArrayList<>()));
    }

    /**
     * 注意:
     *      1. 使用方法引用是可能并不是很好理解, 它会以当前迭代对象为参数, 执行别引用方法的方法体, 返回该方法体的返回值, 所以造成了一种我好象传入Supplier, 但是其实传入的是一个Function.
     *          - 但是需要注意的是, 实例方法和静态方法在被引用时, 原理是不同的.
     *      2. 这是collect的第二个好处: 极简分组.
     */
    @Test
    public void groupByExample() {
        List<UserEntity> list = UserEntity.getList(100);

        //下面的两种方式是完全等价的.
        Map<Integer, List<UserEntity>> collect = list.stream().collect(Collectors.groupingBy(UserEntity::getAge));
        Map<Integer, List<UserEntity>> collect1 = list.stream().collect(Collectors.groupingBy(x -> x.getAge()));

        collect.forEach((k, v) -> System.out.println(k + ": " + v.size()));
    }

    /**
     * 说明:
     *      1. 使用传统方式不适用Collector进行分组.
     * 注意:
     *      1. 在lambda表达式中如果要使用外接的变量, 这个变量就会被隐式修饰为final类型, 这样做是为了避免多线程造成的问题, 因为lambda其实和匿名内部类极为相似, 在匿名内部类中如果使用外部的值,
     *      同样这个值也会被隐式修饰为final类型, 例如jpa的SpecificationExecute接口创建的匿名内类.
     */
    @Test
    public void traditionalGrouping() {
        List<UserEntity> list = UserEntity.getList(100);

        Map<Integer, List<UserEntity>> result = new HashMap<>();
        list.stream().forEach(x -> {
            List<UserEntity> entities = Optional.ofNullable(result.get(x.getAge())).orElseGet(() -> {
                List<UserEntity> temp = new ArrayList<>();
                result.put(x.getAge(), temp);
                return temp;
            });
            entities.add(x);
        });
        //result = null;
        result.forEach((k, v) -> System.out.println(k + ": " + v.size()));
    }

    /**
     * 功能:
     *      使用Collectors的toMap功能.
     * 说明:
     *      1. 如果不传入第三个函数式方法, 创建时就会报错传入默认的(u,v) -> { throw new IllegalStateException(String.format("Duplicate key %s", u)); }, 而这个方法时在碰到同样的key值时执行的,
     *      也就是说, 这个其实是定义一个新旧值的替换规则, 并且是必须定义, 如果不定义使用默认的那个的话, 碰到重复的key就会排除异常.
     */
    @Test
    public void userCollectorsToMap() {
        List<UserEntity> list = UserEntity.getList(100);
        Map<Integer, UserEntity> collect = list.stream().collect(Collectors.toMap(UserEntity::getAge, x -> x, (oldValue, newValue) -> newValue));
        collect.forEach((k, v) -> System.out.println(k + ": " + v));
    }

    /**
     * 说明:
     *      1. 根据一个boolean值对待分区集合进行分区.
     * 输出:
     * ```
     * 结果为:{true: [...], false: [...]}这种格式.
     * ```
     */
    @Test
    public void CollectorAdvanced() {
        List<UserEntity> list = UserEntity.getList(100);
        Map<Boolean, List<UserEntity>> collect = list.stream().collect(Collectors.partitioningBy(x -> x.getAge() >= 58));
        System.out.println(collect);
    }

    @Test
    public void test123() {
        List<UserEntity> list = UserEntity.getList(3);
        System.out.println(list);
        // 可以使用peek对中间数据做处理
        List<UserEntity> collect = list.stream().peek(x -> x.setAge(10000)).collect(Collectors.toList());
        System.out.println(collect);
    }

    /**
     * 功能:
     * <pre>
     *     将集合转换为treemap.
     * </pre>
     * 说明:
     * <pre>
     *     1. 之所以转换为treemap是因为treemap使有序的.
     * </pre>
     */
    @Test
    public void test124() {
        List<UserEntity> list = UserEntity.getList(3);
        TreeMap<Integer, UserEntity> collect = list.stream().collect(Collectors.toMap(UserEntity::getAge, x -> x, (oldValue, newValue) -> newValue, TreeMap::new));
        System.out.println(collect);
    }

    @Test
    public void test121() {
        List<UserEntity> list = UserEntity.getList(3);
        list.get(0).setId(null);
        long count = list.stream().map(UserEntity::getId).filter(Objects::nonNull).distinct().count();
        System.out.println(count);

    }

}